package com.example.service.impl;

import com.example.domain.dto.unit.CreateUnitConversionRequest;
import com.example.domain.dto.unit.CreateUnitRequest;
import com.example.domain.dto.unit.UnitConversionRuleDTO;
import com.example.domain.dto.unit.UnitDTO;
import com.example.domain.entity.Unit;
import com.example.domain.entity.UnitConversionRule;
import com.example.domain.repository.UnitConversionRuleRepository;
import com.example.domain.repository.UnitRepository;
import com.example.exception.BusinessException;
import com.example.exception.ErrorCode;
import com.example.exception.InvalidRequestException;
import com.example.exception.ResourceNotFoundException;
import com.example.service.UnitService;
import com.example.util.IdGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 单位服务实现类
 * 
 * @author Means
 * @since 2025-10-27
 */
@Slf4j
@Service
@Transactional(readOnly = true)
public class UnitServiceImpl implements UnitService {
    
    private final UnitRepository unitRepository;
    private final UnitConversionRuleRepository conversionRuleRepository;
    
    public UnitServiceImpl(UnitRepository unitRepository,
                          UnitConversionRuleRepository conversionRuleRepository) {
        this.unitRepository = unitRepository;
        this.conversionRuleRepository = conversionRuleRepository;
    }
    
    // ==================== 单位管理 ====================
    
    @Override
    @Transactional
    public UnitDTO createUnit(CreateUnitRequest request) {
        log.info("创建单位: code={}, name={}", request.getCode(), request.getName());
        
        // 检查单位代码是否已存在
        if (unitRepository.existsById(request.getCode())) {
            throw new BusinessException(ErrorCode.OPERATION_FAILED, 
                    "单位代码已存在: " + request.getCode());
        }
        
        // 构建单位实体
        Unit unit = new Unit();
        unit.setCode(request.getCode());
        unit.setName(request.getName());
        unit.setDescription(request.getDescription());
        
        // 保存
        unit = unitRepository.save(unit);
        
        log.info("单位创建成功: {}", unit.getCode());
        return UnitDTO.from(unit);
    }
    
    @Override
    public UnitDTO getUnitByCode(String code) {
        log.debug("查询单位: code={}", code);
        
        Unit unit = unitRepository.findById(code)
                .orElseThrow(() -> new ResourceNotFoundException("单位", code));
        
        return UnitDTO.from(unit);
    }
    
    @Override
    public List<UnitDTO> getAllUnits() {
        log.debug("查询所有单位");
        
        List<Unit> units = unitRepository.findAll();
        return units.stream()
                .map(UnitDTO::from)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional
    public void deleteUnit(String code) {
        log.info("删除单位: code={}", code);
        
        // 检查单位是否存在
        if (!unitRepository.existsById(code)) {
            throw new ResourceNotFoundException("单位", code);
        }
        
        // TODO: 检查是否被商品、换算规则等引用，如果被引用则不允许删除
        
        unitRepository.deleteById(code);
        log.info("单位删除成功: {}", code);
    }
    
    @Override
    public boolean existsUnit(String code) {
        return unitRepository.existsById(code);
    }
    
    // ==================== 单位换算规则管理 ====================
    
    @Override
    @Transactional
    public UnitConversionRuleDTO createConversionRule(CreateUnitConversionRequest request) {
        log.info("创建单位换算规则: {} -> {}, factor={}", 
                request.getFromUnit(), request.getToUnit(), request.getFactor());
        
        // 1. 校验源单位和目标单位是否存在
        if (!unitRepository.existsById(request.getFromUnit())) {
            throw new ResourceNotFoundException("源单位", request.getFromUnit());
        }
        if (!unitRepository.existsById(request.getToUnit())) {
            throw new ResourceNotFoundException("目标单位", request.getToUnit());
        }
        
        // 2. 检查是否已存在相同的换算规则
        conversionRuleRepository.findByFromUnitAndToUnit(request.getFromUnit(), request.getToUnit())
                .ifPresent(rule -> {
                    throw new BusinessException(ErrorCode.OPERATION_FAILED,
                            String.format("换算规则已存在: %s -> %s", 
                                    request.getFromUnit(), request.getToUnit()));
                });
        
        // 3. 构建换算规则实体
        UnitConversionRule rule = new UnitConversionRule();
        rule.setId(IdGenerator.generate());
        rule.setFromUnit(request.getFromUnit());
        rule.setToUnit(request.getToUnit());
        rule.setFactor(request.getFactor());
        rule.setEnabled(request.getEnabled() != null && request.getEnabled());
        
        // 4. 保存
        rule = conversionRuleRepository.save(rule);
        
        log.info("换算规则创建成功: id={}", rule.getId());
        return UnitConversionRuleDTO.from(rule);
    }
    
    @Override
    public UnitConversionRuleDTO getConversionRule(String fromUnit, String toUnit) {
        log.debug("查询换算规则: {} -> {}", fromUnit, toUnit);
        
        return conversionRuleRepository.findByFromUnitAndToUnit(fromUnit, toUnit)
                .map(UnitConversionRuleDTO::from)
                .orElse(null);
    }
    
    @Override
    public List<UnitConversionRuleDTO> getConversionRulesByUnit(String unitCode) {
        log.debug("查询单位相关的所有换算规则: {}", unitCode);
        
        // 查询以该单位为源单位或目标单位的所有规则
        List<UnitConversionRule> fromRules = conversionRuleRepository.findByFromUnit(unitCode);
        List<UnitConversionRule> toRules = conversionRuleRepository.findByToUnit(unitCode);
        
        // 合并结果并去重
        return Stream.concat(fromRules.stream(), toRules.stream())
                .distinct()
                .map(UnitConversionRuleDTO::from)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional
    public void deleteConversionRule(String ruleId) {
        log.info("删除换算规则: id={}", ruleId);
        
        if (!conversionRuleRepository.existsById(ruleId)) {
            throw new ResourceNotFoundException("换算规则", ruleId);
        }
        
        conversionRuleRepository.deleteById(ruleId);
        log.info("换算规则删除成功: {}", ruleId);
    }
    
    @Override
    @Transactional
    public void updateConversionRuleStatus(String ruleId, boolean enabled) {
        log.info("更新换算规则状态: id={}, enabled={}", ruleId, enabled);
        
        UnitConversionRule rule = conversionRuleRepository.findById(ruleId)
                .orElseThrow(() -> new ResourceNotFoundException("换算规则", ruleId));
        
        rule.setEnabled(enabled);
        conversionRuleRepository.save(rule);
        
        log.info("换算规则状态更新成功: {}", ruleId);
    }
    
    // ==================== 单位换算计算 ====================
    
    @Override
    public BigDecimal convertQuantity(BigDecimal value, String fromUnit, String toUnit) {
        log.debug("换算数量: value={}, {} -> {}", value, fromUnit, toUnit);
        
        // 1. 参数校验
        if (value == null || value.compareTo(BigDecimal.ZERO) < 0) {
            throw new InvalidRequestException(ErrorCode.PARAMETER_ERROR, "数量值必须为非负数");
        }
        
        // 2. 如果源单位和目标单位相同，直接返回原值
        if (fromUnit.equals(toUnit)) {
            return value;
        }
        
        // 3. 查询换算规则
        UnitConversionRule rule = conversionRuleRepository
                .findByFromUnitAndToUnitAndEnabled(fromUnit, toUnit, true)
                .orElseThrow(() -> new BusinessException(ErrorCode.CONVERSION_NOT_SUPPORTED,
                        String.format("未找到有效的换算规则: %s -> %s", fromUnit, toUnit)));
        
        // 4. 执行换算
        BigDecimal result = value.multiply(rule.getFactor());
        
        log.debug("换算结果: {} {} = {} {}", value, fromUnit, result, toUnit);
        return result;
    }
    
    @Override
    public boolean canConvert(String fromUnit, String toUnit) {
        // 相同单位总是可以换算
        if (fromUnit.equals(toUnit)) {
            return true;
        }
        
        // 检查是否存在有效的换算规则
        return conversionRuleRepository
                .findByFromUnitAndToUnitAndEnabled(fromUnit, toUnit, true)
                .isPresent();
    }
}

